package com.zl.rbac.util;

import com.zl.rbac.entity.Permission;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 扫描指定包下所有的类上的所有添加@RequiredPermission注解的方法
 *   解析为一个一个Permission对象
 *
 * @ClassName: PermissionAnnotationUtils
 * @author: suke
 * @since: 2021/1/11 11:44
 *
 *   //1.得到指定包下所有类的Class对象(反射)
         //   1)  类名.class
         //   2)  对象.getClass()
        //   3)  Class.forName("包.类")
       //   4) 借助Spring的注解解析器  ResourcePatternResolver spring工具类，可以获取指定路径下的全部类

    // 2.通过Class对象得到这个类的所有的方法的信息, method对象
    // 3.判断方法上是否有@RequiredPermission注解, 有解析, 获取这个注解的value, 通过方法对象得到权限表达式, 创建对应权限对象
 */
@Component
public class PermissionAnnotationUtils {

    @Autowired
    private ResourceLoader resourceLoader;

    private  final String RESOURCE_PATTERN = "/**/*.class";  //获取某个包下所有的类的class文件

    /**
     *
     * @Title: 获取指定包下的指定的注解的所有类的Class对象
     * @param basePackage: 指定的包
     * @param tagAnnotationClass: 指定的注解
     * @return java.util.Map<java.lang.String,java.lang.Class>
     * @author: suke
     * @since: 2021/1/7 22:36
     */
    public  Map<String,Class>   getClassByPackage(String  basePackage,  Class tagAnnotationClass){
        Map<String, Class> classMap = new HashMap<String, Class>();
        //spring工具类，可以获取指定路径下的全部类
        ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        // basePackage="com.zl.rbac.web.controller"
        //   ClassUtils.convertClassNameToResourcePath(basePackage) --> com/zl/rbac/web/controller
        // pattern="classpath*:com/zl/rbac/web/controller/**/*.class"
        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(basePackage) + RESOURCE_PATTERN;
        try {
            // 加载class文件,  变成对应Resource对象
            Resource[] resources =  resourcePatternResolver.getResources(pattern);
            //MetadataReader 的工厂类
            MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
            for (Resource r : resources) {
                MetadataReader reader = readerfactory.getMetadataReader(r);
                //扫描到的class  classname得到类的全限定名:  包名.类名
                String classname = reader.getClassMetadata().getClassName();
                //得到对应Class对象
                Class<?> clazz = Class.forName(classname);
                if(tagAnnotationClass != null){
                    //tagAnnotationClass:  注解类的Class类型
                    // isAnnotationPresent() 判断这个类上是否有指定注解
                    if(clazz.isAnnotationPresent(tagAnnotationClass)){
                        classMap.put(classname,clazz);
                    }
                }else{
                    classMap.put(classname,clazz);
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return classMap;
    }
    
    /**
     *  扫描指定包下,指定注解的类的指定的方法注解的方法信息,  变成一个权限对象
     * @Title: getMethodInfoByAnnoation
     * @param    basePackage  指定包
     * @param    classAnnotation  指定类上的注解
     * @param    methodAnnotation  指定方法上的数据
     * @return java.util.Map<java.lang.String,java.util.List<Permission>>
     * @author: suke
     * @since: 2021/1/11 13:45
     */
    public  Map<String,List<Permission>> getMethodInfoByAnnoation(String basePackage, Class classAnnotation, Class methodAnnotation){
        Map<String,Class> classMap =  getClassByPackage(basePackage,classAnnotation);
        Map<String,List<Permission>> permissionMap = new HashMap<>();
        Set<Map.Entry<String,Class>> entrySet = classMap.entrySet();
        entrySet.forEach((entry)->{
            Class clazz = entry.getValue();
            String className = entry.getKey();

            //通过反射,得到这个类的所有的方法  getMethods() 得到public修饰的方法, 包含从父类继承的方法public
            //              getDeclaredMethods()  得到本类的所有方法,不包括从父类继承的
            Method[]  methods = clazz.getDeclaredMethods();
            List<Permission> permissions = new ArrayList<>();
            Arrays.stream(methods).forEach((method)->{
                if(method.isAnnotationPresent(methodAnnotation)){
                    Permission permission = new Permission();
                    permission.setExpression(className+":"+method.getName());
                    //AnnotationUtils  Spring提供的一个注解的工具类  getAnnotationAttributes() 获取指定注解的所有参数
                    //getAnnotation(Class对象, 注解Class对象) 获取指定Class对象上指定的注解
                    Map<String,Object> attrMap = AnnotationUtils.getAnnotationAttributes(AnnotationUtils.getAnnotation(method,methodAnnotation));
                    if(attrMap.containsKey("value")){
                        permission.setName(attrMap.get("value").toString());;
                    }
                    permissions.add(permission);
                }
                permissionMap.put(className,permissions);
            });
        });

        return permissionMap;
    }
}
